package com.quectel.core.module.alarm.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.quectel.constant.core.alarm.AlarmConstants;
import com.quectel.constant.global.SqlConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.constants.RedisCacheConstants;
import com.quectel.core.module.alarm.dao.AlarmRuleConditionDao;
import com.quectel.core.module.alarm.dao.AlarmRuleDao;
import com.quectel.core.module.alarm.dto.*;
import com.quectel.core.module.alarm.entity.AlarmRuleConditionEntity;
import com.quectel.core.module.alarm.entity.AlarmRuleEntity;
import com.quectel.core.module.alarm.service.AlarmRuleConditionService;
import com.quectel.core.module.alarm.service.AlarmRuleDeviceConditionService;
import com.quectel.core.module.alarm.service.AlarmRuleService;
import com.quectel.core.module.alarm.service.AlarmRuleTenantConditionService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author caolu
 * @email louis.cao@quectel.com
 * @date 2019-05-31 12:25:26
 */
@DubboService
public class AlarmRuleServiceImpl implements AlarmRuleService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AlarmRuleServiceImpl.class);

    @Autowired
    private AlarmRuleDao alarmRuleDao;
    @Autowired
    private AlarmRuleConditionDao alarmConditionDao;
    @Autowired
    private AlarmRuleConditionService alarmRuleConditionService;
    @Autowired
    private AlarmRuleTenantConditionService alarmRuleTenantConditionService;
    @Autowired
    private AlarmRuleDeviceConditionService alarmRuleDeviceConditionService;

    @Override
    public List<HandAlarmRuleInfo> handDeviceAlarm(DeviceDto deviceDto, Map<String, Object> recData) {
        List<HandAlarmRuleInfo> result = new ArrayList<>();

        List<AlarmRuleDto> alarmRuleDtoList = getCachedAlarmRuleByDevice(deviceDto);
        if (alarmRuleDtoList == null || alarmRuleDtoList.size() <= 0) {
            //没有告警
            return result;
        }
        for (AlarmRuleDto alarmRuleDto : alarmRuleDtoList) {

            HandAlarmRuleInfo handAlarmRuleInfo = new HandAlarmRuleInfo();
            handAlarmRuleInfo.setAlarmRuleDto(alarmRuleDto);
            handAlarmRuleInfo.setAlarmConformLevel(AlarmConstants.ALARM_UNKNOWN);//默认未知

            boolean isContainsAllField = true;
            if (alarmRuleDto.getAlarmConditionList() != null && alarmRuleDto.getAlarmConditionList().size() > 0) {
                for (AlarmRuleConditionDto alarmRuleConditionDto : alarmRuleDto.getAlarmConditionList()) {
                    //判断规则条件里边的参数是否存在于传进来的数据
                    // 这里需要说明的是比如我配置了status字段  而传入进来的字段没有  那么下面的程序会认为不符合告警 就把告警恢复到正常 这样是错误的所以必须加上这段代码
                    if (recData.get(alarmRuleConditionDto.getDeviceColumnEntityName()) == null) {
                        //跳出内层循环
                        isContainsAllField = false;
                        break;
                    }
                }
            } else {
                isContainsAllField = false;
            }

            if (!isContainsAllField) {
                //进入下一圈

                result.add(handAlarmRuleInfo);

                continue;
            }

            boolean conformRule = isConformRule(alarmRuleDto, recData);
            if (conformRule) {
                //符合告警
                handAlarmRuleInfo.setAlarmConformLevel(AlarmConstants.ALARM_YES);

            } else {
                handAlarmRuleInfo.setAlarmConformLevel(AlarmConstants.ALARM_NO);
            }
            result.add(handAlarmRuleInfo);
        }
        return result;
    }

    @Override
    public AlarmRuleDto selectByCode(String code) {
        AlarmRuleEntity entity = alarmRuleDao.selectOne(
                new LambdaQueryWrapper<AlarmRuleEntity>()
                        .eq(AlarmRuleEntity::getCode, code)
                        .last(SqlConstants.MYSQL_LIMIT_ONE)
        );
        return CopyUtils.copyObj(entity, AlarmRuleDto.class);
    }

    /**
     * 是否符合告警规则
     *
     * @param alarmRuleDto
     * @param recData
     * @return
     */
    @Override
    public boolean isConformRule(AlarmRuleDto alarmRuleDto, Map<String, Object> recData) {
        List<AlarmRuleConditionDto> alarmConditionList = alarmRuleDto.getAlarmConditionList();
        if (CollectionUtils.isEmpty(alarmConditionList)) {
            //条件为空则不符合告警
            return false;
        }
        //判断条件是否满足
        for (AlarmRuleConditionDto alarmRuleConditionDto : alarmConditionList) {
            String quota = alarmRuleConditionDto.getQuota();
            String condition = alarmRuleConditionDto.getCondition();
            String columnName = alarmRuleConditionDto.getDeviceColumnEntityName();
            //收到的值
            Object recColValue = recData.get(columnName);
            if (recColValue == null || "".equals(recColValue + "")) {
                //传入字段为空 不足以判断所以不告警
                return false;
            }

//			全部满足才是告警有一条不满足则不是告警
            if ("=".equals(condition)) {
                //条件是等于
                if (!Objects.equals(quota, recColValue + "")) {
                    return false;
                }
            } else {
                //其他情况则是 数字对比
                BigDecimal quotaNum = new BigDecimal(quota);
                BigDecimal recColNum = new BigDecimal(recColValue + "");
                if (">".equals(condition)) {
                    if (!(recColNum.compareTo(quotaNum) > 0)) {
                        return false;
                    }
                } else if (">=".equals(condition)) {
                    if (!(recColNum.compareTo(quotaNum) >= 0)) {
                        return false;
                    }
                } else if (("<".equals(condition))) {
                    if (!(recColNum.compareTo(quotaNum) < 0)) {
                        return false;
                    }
                } else if ("<=".equals(condition)) {
                    if (!(recColNum.compareTo(quotaNum) <= 0)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    @Override
    public List<AlarmRuleDto> getCachedAlarmRuleByDevice(DeviceDto deviceDto) {

        return CacheKit.cacheToRedis(
                () -> {
                    //查询该设备类型下的告警
                    return getAlarmRuleByDevice(deviceDto);
                },
                RedisCacheConstants.DEVICE_ALARM_RULES + "_" + deviceDto.getId(),

                SystemConstants.NOT_NULL_CACHE_EXPIRE_SECONDS

        );
    }


    @Override
    public List<AlarmRuleDto> getAlarmRuleByDevice(DeviceDto deviceDto) {

        //查询该设备类型下的告警
        List<AlarmRuleEntity> alarmRuleEntities = alarmRuleDao.selectList(
                new LambdaQueryWrapper<AlarmRuleEntity>()
                        .eq(AlarmRuleEntity::getDeviceTypeId, deviceDto.getDeviceTypeId())
                        .eq(AlarmRuleEntity::getStatus, SystemConstants.NORMAL)
        );
        List<AlarmRuleDto> result = new ArrayList<>();

        List<AlarmRuleDto> list = CopyUtils.copyList(alarmRuleEntities, AlarmRuleDto.class);
        if (list != null) {
            for (AlarmRuleDto alarmRuleDto : list) {
                if (!SystemConstants.YES.equals(deviceDto.getAlarmSwitch())) {
                    continue;
                }
                //先查询设备是否配置了告警阈值
                if (SystemConstants.YES.equals(deviceDto.getCustomRule())) {
                    //先查询设备是否配置了告警阈值
                    AlarmRuleDeviceConditionDto alarmRuleDeviceConditionDto = alarmRuleDeviceConditionService.selectByDeviceIdAndRuleId(deviceDto.getId(), alarmRuleDto.getId());
                    if (alarmRuleDeviceConditionDto != null) {
                        AlarmRuleDto tenantRule = JacksonUtils.parse(alarmRuleDeviceConditionDto.getAlarmRuleConditionJson(), AlarmRuleDto.class);
                        if (SystemConstants.NORMAL.equals(tenantRule.getStatus().byteValue())) {
                            //用设备单独的条件对原始告警模板进行替换
//                                    alarmRuleDto.setLevel(tenantRule.getLevel());
                            alarmRuleDto.setStatus(tenantRule.getStatus());
                            alarmRuleDto.setName(tenantRule.getName());
                            alarmRuleDto.setAlarmConditionList(tenantRule.getAlarmConditionList());
                            result.add(alarmRuleDto);
                        } else {
                            //不做任何处理 这里为方便阅读
                        }
                        continue;
                    }
                }


                //查询租户的配置
                AlarmRuleTenantConditionDto alarmRuleTenantConditionDto = alarmRuleTenantConditionService.selectByTenantIdAndRuleId(deviceDto.getTenantId(), alarmRuleDto.getId());
                if (alarmRuleTenantConditionDto != null) {

                    AlarmRuleDto tenantRule = JacksonUtils.parse(alarmRuleTenantConditionDto.getAlarmRuleConditionJson(), AlarmRuleDto.class);
                    if (SystemConstants.NORMAL.equals(tenantRule.getStatus().byteValue())) {
                        //用租户的条件对原始告警模板进行替换
                        alarmRuleDto.setLevel(tenantRule.getLevel());
                        alarmRuleDto.setStatus(tenantRule.getStatus());
                        alarmRuleDto.setName(tenantRule.getName());
                        alarmRuleDto.setAlarmConditionList(tenantRule.getAlarmConditionList());
                        result.add(alarmRuleDto);
                    } else {
                        //不做任何处理 这里为方便阅读
                    }
                    continue;
                }
                //租户和设备没有自定义配置 采取超级组设置的模板
                alarmRuleDto.setAlarmConditionList(alarmRuleConditionService.selectByRuleId(alarmRuleDto.getId()));
                result.add(alarmRuleDto);
            }
        }
        return result;
    }

    @Override
    public List<AlarmRuleDto> getAlarmRuleByDeviceTypeId(Long tenantId, Long deviceTypeId) {


        //查询该设备类型下的告警
        List<AlarmRuleEntity> alarmRuleEntities = alarmRuleDao.selectList(
                new LambdaQueryWrapper<AlarmRuleEntity>()
                        .eq(AlarmRuleEntity::getDeviceTypeId, deviceTypeId)
                        .eq(AlarmRuleEntity::getStatus, SystemConstants.NORMAL)
        );
        List<AlarmRuleDto> result = new ArrayList<>();

        List<AlarmRuleDto> list = CopyUtils.copyList(alarmRuleEntities, AlarmRuleDto.class);
        if (list != null) {
            for (AlarmRuleDto alarmRuleDto : list) {
                //查询租户的配置
                AlarmRuleTenantConditionDto alarmRuleTenantConditionDto = alarmRuleTenantConditionService.selectByTenantIdAndRuleId(tenantId, alarmRuleDto.getId());
                if (alarmRuleTenantConditionDto != null) {

                    AlarmRuleDto tenantRule = JacksonUtils.parse(alarmRuleTenantConditionDto.getAlarmRuleConditionJson(), AlarmRuleDto.class);
                    if (SystemConstants.NORMAL.equals(tenantRule.getStatus().byteValue())) {
                        //用租户的条件对原始告警模板进行替换
                        alarmRuleDto.setLevel(tenantRule.getLevel());
                        alarmRuleDto.setStatus(tenantRule.getStatus());
                        alarmRuleDto.setName(tenantRule.getName());
                        alarmRuleDto.setAlarmConditionList(tenantRule.getAlarmConditionList());
                        result.add(alarmRuleDto);
                    } else {
                        //不做任何处理 这里为方便阅读
                    }
                    continue;


                }
                //租户和设备没有自定义配置 采取超级组设置的模板
                alarmRuleDto.setAlarmConditionList(alarmRuleConditionService.selectByRuleId(alarmRuleDto.getId()));
                result.add(alarmRuleDto);
            }
        }
        return result;
    }

    @Override
    public List<AlarmRuleDto> queryList(Map<String, Object> params) {
        List<AlarmRuleEntity> list = alarmRuleDao.queryList(params);
        List<AlarmRuleDto> result = CopyUtils.copyList(list, AlarmRuleDto.class);
        for (AlarmRuleDto alarmRuleDto : result) {
            paddingField(alarmRuleDto);
        }
        return result;
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return alarmRuleDao.queryTotal(params);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long save(AlarmRuleDto dto) {

        AlarmRuleEntity entity = CopyUtils.copyObj(dto, AlarmRuleEntity.class);
        entity.setId(Snowflake.nextId());
        alarmRuleDao.insert(entity);

        dto.setId(entity.getId());
        saveRuleConditionRelation(dto);

        return entity.getId();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateById(AlarmRuleDto dto) {
        AlarmRuleEntity entity = CopyUtils.copyObj(dto, AlarmRuleEntity.class);
        alarmRuleDao.updateById(entity);

        saveRuleConditionRelation(dto);
    }

    private void saveRuleConditionRelation(AlarmRuleDto dto) {

        alarmConditionDao.delete(new QueryWrapper<AlarmRuleConditionEntity>().eq("rule_id", dto.getId()));

        for (AlarmRuleConditionDto alarmRuleConditionDto : dto.getAlarmConditionList()) {
            alarmRuleConditionDto.setId(Snowflake.nextId());
            alarmRuleConditionDto.setRuleId(dto.getId());
            alarmConditionDao.insert(CopyUtils.copyObj(alarmRuleConditionDto, AlarmRuleConditionEntity.class));
        }
    }

    @Override
    public AlarmRuleDto selectById(Long id) {
        AlarmRuleEntity entity = alarmRuleDao.selectById(id);
        AlarmRuleDto alarmRuleDto = CopyUtils.copyObj(entity, AlarmRuleDto.class);
        paddingField(alarmRuleDto);
        return alarmRuleDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Long id) {
        alarmRuleDao.deleteById(id);
        alarmConditionDao.delete(new QueryWrapper<AlarmRuleConditionEntity>().eq("rule_id", id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }

    @Override
    public List<AlarmRuleDto> queryAll(Map<String, Object> params) {
        List<AlarmRuleEntity> list = alarmRuleDao.queryAll(params);
        return CopyUtils.copyList(list, AlarmRuleDto.class);
    }


    private void paddingField(AlarmRuleDto alarmRuleDto) {
        if (alarmRuleDto == null) {
            return;
        }
        List<AlarmRuleConditionDto> list = alarmRuleConditionService.selectByRuleId(alarmRuleDto.getId());
        alarmRuleDto.setAlarmConditionList(list);
    }
}
